/**
 * 
 */
package jmine.tec.utils.date;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;

import javax.persistence.Embeddable;

import jmine.tec.utils.Tuple;
import jmine.tec.utils.UnmodifiableTuple;

/**
 * @author takeshi
 * @version 24/08/2007
 */
@Embeddable
public class PastDatePeriod implements Serializable {

    private static final long serialVersionUID = 5579549320597113073L;

    private DatePeriodUnit unit;

    private int value;

    /**
     * C'tor
     */
    public PastDatePeriod() {
        this(0, DatePeriodUnit.DAYS);
    }

    /**
     * C'tor
     * 
     * @param value the amount of time
     * @param unit the {@link DatePeriodUnit}
     */
    public PastDatePeriod(final int value, final DatePeriodUnit unit) {
        super();
        this.unit = unit;
        this.value = value;
    }

    /**
     * @param obj the other object
     * @return boolean
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }
        final PastDatePeriod other = (PastDatePeriod) obj;
        if (this.unit == null) {
            if (other.unit != null) {
                return false;
            }
        } else if (!this.unit.equals(other.unit)) {
            return false;
        }
        if (this.value != other.value) {
            return false;
        }
        return true;
    }

    /**
     * @return the unit
     */
    public DatePeriodUnit getUnit() {
        return this.unit;
    }

    /**
     * @return the value
     */
    public int getValue() {
        return this.value;
    }

    /**
     * Gets the start date of this {@link PastDatePeriod}, according to the {@link DatePeriodUnit} and {@link #getValue()} attributes, using
     * the given date as a reference.
     * <p>
     * Unit considerations:
     * <ul>
     * <li> {@link DatePeriodUnit#DAYS}: the period is of a single day, meaning the start date and the end date will differ of a single day.</li>
     * <li> {@link DatePeriodUnit#WEEKS}: the period consist of a week, from monday to monday.</li>
     * <li> {@link DatePeriodUnit#MONTHS}: the period consist of a month, from the first day of the starting month to the first day of the
     * next month</li>
     * <li> {@link DatePeriodUnit#WEEKS}: the period consist of an year, from the first day of the starting year to the first day of the next
     * year</li>
     * </ul>
     * 
     * @param reference {@link Date}
     * @return {@link Date}
     */
    public Date getStartDate(final Date reference) {
        Calendar cal = this.getStartCalendarInstance(reference);
        return cal.getTime();
    }

    /**
     * @param reference the reference {@link Date}
     * @return {@link Calendar}
     */
    private Calendar getStartCalendarInstance(final Date reference) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(reference);
        DateUtil.clearTimeFields(cal);
        cal.add(this.unit.getCalendarField(), -this.value);
        switch (this.unit) {
        case DAYS:
            break;

        case WEEKS:
            cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
            break;

        case MONTHS:
            cal.set(Calendar.DAY_OF_MONTH, 1);
            break;

        case YEARS:
            cal.set(Calendar.DAY_OF_YEAR, 1);
            break;

        default:
            break;
        }
        return cal;
    }

    /**
     * Gets the end date of this {@link PastDatePeriod}, according to the {@link DatePeriodUnit} and {@link #getValue()} attributes, using
     * the given date as a reference.
     * 
     * @param reference {@link Date}
     * @return {@link Date}
     */
    public Date getEndDate(final Date reference) {
        Calendar start = this.getStartCalendarInstance(reference);
        start.add(this.unit.getCalendarField(), 1);
        return start.getTime();
    }

    /**
     * Gets the start date of this period, taking the current time as a reference
     * 
     * @return {@link Date}
     */
    public Date getStartDateFromNow() {
        return this.getStartDate(new Date());
    }

    /**
     * Gets the bounds as a {@link Tuple}
     * 
     * @param reference the reference {@link Date}
     * @return {@link Tuple} of {@link Date}
     */
    public Tuple<Date, Date> getBounds(final Date reference) {
        return UnmodifiableTuple.newTuple(this.getStartDate(reference), this.getEndDate(reference));
    }

    /**
     * Gets the bounds as a {@link Tuple} using the current time as a reference
     * 
     * @return {@link Tuple} of {@link Date}
     */
    public Tuple<Date, Date> getBoundsFromNow() {
        return this.getBounds(new Date());
    }

    /**
     * @return {@link Date}
     */
    public Date getEndDateFromNow() {
        return this.getEndDate(new Date());
    }

    /**
     * @return hashcode
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.unit == null) ? 0 : this.unit.hashCode());
        result = prime * result + this.value;
        return result;
    }

    /**
     * @param unit the unit to set
     * @deprecated should be used as an unmodifiable. This method is here for completeness.
     */
    @Deprecated
    public void setUnit(final DatePeriodUnit unit) {
        this.unit = unit;
    }

    /**
     * @param value the value to set
     * @deprecated should be used as an unmodifiable. This method is here for completeness.
     */
    @Deprecated
    public void setValue(final int value) {
        this.value = value;
    }

}
